﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace StackExchange.Redis.Extensions.SlidingWindow
{
    public static class IDatabaseExtension
    {
        private const string SET_NAME = "DefaultSet";
        private static readonly DateTime BASE_TIME = DateTime.Parse("2021-01-01T00:00:00.000");

        public static double SetKey(this IDatabase redisDb, string key)
        {
            double score = (DateTime.UtcNow - BASE_TIME).TotalMilliseconds;

            var currentScore = redisDb.SortedSetScore(SET_NAME, key);

            if (currentScore.HasValue)
            {
                var incrementalScore = score - currentScore.Value;

                return redisDb.SortedSetIncrement(SET_NAME, key, incrementalScore);
            }
            else
            {
                redisDb.SortedSetAdd(SET_NAME, key, score);

                return score;
            }
        }

        public static Dictionary<string, double> GetTopKeys(this IDatabase redisDb, int tops, int slidingSeconds)
        {
            Dictionary<string, double> dict = new Dictionary<string, double>();

            double score = (DateTime.UtcNow - BASE_TIME).TotalMilliseconds - (slidingSeconds * 1000);

            var result = redisDb.SortedSetRangeByScoreWithScores(SET_NAME, stop: score, skip: 0, take: tops - 1);

            if(result.Any())
            {
                foreach(var r in result)
                {
                    dict[r.Element.ToString()] = r.Score;
                }
            }

            return dict;
        }

        public static bool SetRemoveWithScore(this IDatabase redisDb, string key, double score)
        {
            const string script = @"local currentScore = tonumber(redis.call('ZSCORE', KEYS[1], ARGV[1]));
                if (currentScore == tonumber(ARGV[2]) ) then return redis.call('ZREM', KEYS[1], ARGV[1]) else return 0 end;";

            var result = redisDb.ScriptEvaluate(script, new RedisKey[] { SET_NAME }, new RedisValue[] { key, score });

            if(!result.IsNull)
            {
                if (int.Parse(result.ToString()) > 0)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }

            throw new Exception("Redis commands execute failed");
        }
    }
}
